package com.allen.app.common.config.swagger.reader;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.allen.app.common.config.swagger.common.ApiHideBodyProperty;
import com.allen.app.common.config.swagger.common.ApiParamType;
import com.allen.app.common.config.swagger.common.ModelPropertyUtil;
import com.allen.app.common.utils.CheckObjectUtils;
import com.allen.app.common.utils.Jackson;
import com.fasterxml.classmate.ResolvedType;
import io.swagger.annotations.ApiModel;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

/**
 * 修改RequestBody的ModelRef设置，并记录到ApiReadContext
 *
 * @author Leon (234239150@qq.com) created in 16:33 18.9.11
 */
@Slf4j
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 100)
public class ApiParameterReader implements ParameterBuilderPlugin {

    private final ApiReaderContext apiReaderContext;

    public ApiParameterReader(ApiReaderContext apiReaderContext) {
        this.apiReaderContext = apiReaderContext;
    }

    @Override
    public void apply(ParameterContext parameterContext) {
        ResolvedMethodParameter parameter = parameterContext.resolvedMethodParameter();
        ResolvedType resolvedType = parameter.getParameterType();
        ModelRef modelRef = new ModelRef(RandomUtil.randomNumbers(16));

        if (parameter.hasParameterAnnotation(RequestBody.class)) {
            ApiHideBodyProperty hideBodyProperty = parameterContext.getOperationContext().findAnnotation(ApiHideBodyProperty.class).orNull();
            if (Objects.nonNull(hideBodyProperty) && hideBodyProperty.value().length > 0) {
                String name = parameter.defaultName().or(modelRef.getType());
                parameterContext.parameterBuilder().parameterType(ApiParamType.BODY).modelRef(modelRef).name(name);
                this.apiReaderContext.add(modelRef.getType(), parameter.getParameterType(), hideBodyProperty.value(), hideBodyProperty.type());
                apiReaderContext.addenumAndHideParameter(modelRef.getType(), parameter.getParameterType());
            }
            if (!apiReaderContext.getEnumAndHideParameter().containsKey(modelRef.getType())) {
                Field[] declaredFields = resolvedType.getErasedType().getDeclaredFields();
                for (Field field : declaredFields) {
                    if (field.getType().isEnum()) {
                        String name = parameter.defaultName().or(modelRef.getType());
                        parameterContext.parameterBuilder().parameterType(ApiParamType.BODY).modelRef(modelRef).name(name);
                        apiReaderContext.addenumAndHideParameter(modelRef.getType(), parameter.getParameterType());
                        break;
                    }
                }
            }
        } else if (parameter.hasParameterAnnotation(RequestParam.class) || parameter.hasParameterAnnotation(
            PathVariable.class)) {
            if (resolvedType.getErasedType().isEnum()) {
                ApiModel apiModel = resolvedType.getErasedType().getAnnotation(ApiModel.class);
                String value = resolvedType.getErasedType().getSimpleName();
                if (Objects.nonNull(apiModel)) {
                    value = CheckObjectUtils.nonNull(apiModel.description()) ? apiModel.description() : apiModel.value();
                }
                List<Map<String, Object>> enumValues = ModelPropertyUtil.getEnumValues(resolvedType.getErasedType());
                String formatValue = StrUtil.format("{}:\n{}", value, Jackson.json(enumValues));
                formatValue = formatValue.replaceAll("},","},\\\n");
                String parameterType = parameter.hasParameterAnnotation(PathVariable.class) ? ApiParamType.PATH : ApiParamType.QUERY;
                parameterContext.parameterBuilder().parameterType(parameterType).description(formatValue);
            }
        }
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }


}
